// SimpleRegAllocator.cpp 
// Implement class SimpleRegAllocator
// Transplanted by lixiaolong.
// iterated use 0-7 physical register.
#include "crossbit/SimpleRegAllocator.h"
#include "crossbit/VBlock.h"
#include "crossbit/VInst.h"

using namespace crossbit;

void SimpleRegAllocator::init(VBlock *vb)
{
								struct RegUsage ru;
								vinst_seq = 0;
								ra_cal_treg=0;
								ru.status = RegAllocator::FREE;
								ru.mapped_to = -1;
								ru.inst = 0;
								for (XTRegNum i = 0; i < ra_num_of_reg; ++i)
								{
																ra_reg_table[i]=ru;
								}
								UTIL::XTMemset(ra_vreg_spilledIn,0,sizeof(ra_vreg_spilledIn));
}

void SimpleRegAllocator::phaseTask()
{
}

// Allocate target register for virtual register
XTRegNum SimpleRegAllocator::regAlloc(XTRegNum vreg, RegAccessMode mode)
{
								XTRegNum alloc;
								for(XTRegNum i=0;i<ra_num_of_reg;i++)
								{
																if(ra_reg_table[i].status==ALLOCATED && ra_reg_table[i].mapped_to==vreg)
																{
																								if(ra_vreg_spilledIn[vreg]==false&&mode==USE)
																								{
																																regSpillIn(vreg,i);
																																ra_vreg_spilledIn[vreg]=true;
																								}
																								ra_reg_table[i].inst=vinst_seq;
																								return i;
																}
								} 
								for(XTRegNum i=0;i<ra_num_of_reg;i++)
								{  
																if(ra_reg_table[i].status==FREE)
																{
																								if(ra_vreg_spilledIn[vreg]==false&&mode==USE)
																								{ 
																																regSpillIn(vreg,i);
																																ra_vreg_spilledIn[vreg]=true;
																								}
																								ra_reg_table[i].status=ALLOCATED;
																								ra_reg_table[i].mapped_to=vreg;
																								ra_reg_table[i].inst=vinst_seq;
																								return i;
																}
								} 

								// find ALLOCATED target register whose mapped virtual regiser 
								// using vreg%8 to allocate the target regs.
								for(XTInt32 cn=0;cn<8;cn++)
								{
																struct RegUsage tru=ra_reg_table[ra_cal_treg];
																if (tru.status!=RegAllocator::ALLOCATED||tru.inst==vinst_seq)//RESERVED register can't be used&&The treg who is using in the current instruction.
																								ra_cal_treg=(ra_cal_treg+1)%8;
																else
																{
																								break;
																}
								}
								alloc=ra_cal_treg;
								ra_cal_treg=(ra_cal_treg+1)%8;
								regSpillOut(alloc);
								if (mode==USE)
								{
																regSpillIn(vreg, alloc);
																ra_vreg_spilledIn[vreg]=true;
								}
								ra_reg_table[alloc].status = ALLOCATED;
								ra_reg_table[alloc].mapped_to = vreg;
								ra_reg_table[alloc].inst = vinst_seq; 
								return alloc;

}

// Force allocate target register "expect" to "vreg"
//
// Algorithms:
//  if (vreg is already allocated to expect)
//	return immediately
//  else 
//	empty "expect" register for "Vreg";
//	if (vreg is currently allocated to another target register)
//	    move it to "expect"
//	if (vreg is spilled)
//	    load vreg from spill pool to expect
//	...
// 
XTRegNum SimpleRegAllocator::regAllocForce(XTRegNum vreg, XTRegNum expect, RegAccessMode mode)
{
								RegUsage etru=ra_reg_table[expect];
								assert(etru.status != RegAllocator::RESERVED); // dead-lock

								if (etru.status == RegAllocator::ALLOCATED) 
								{
																if(etru.mapped_to==vreg)
																{
																								ra_reg_table[expect].inst=vinst_seq;
																								if(ra_vreg_spilledIn[vreg]==false&&mode==USE)
																								{
																																regSpillIn(vreg,expect);
																																ra_vreg_spilledIn[vreg]=true;
																								}
																								return expect;
																}
																regSpillOut(expect);
								}
								for(XTRegNum i=0;i<ra_num_of_reg;i++)
								{
																if(ra_reg_table[i].status==ALLOCATED&&ra_reg_table[i].mapped_to==vreg)
																{
																								(*ra_cb_reg_to_reg)(i,expect);
																								ra_reg_table[expect].status=ALLOCATED;
																								ra_reg_table[expect].inst=vinst_seq;
																								ra_reg_table[expect].mapped_to=vreg;
																								ra_reg_table[i].status=FREE;
																								ra_reg_table[i].mapped_to=-1;
																								return expect;
																}
								}
								ra_vreg_spilledIn[vreg]=false;
								if (mode==USE) 
								{
																regSpillIn(vreg, expect);
																ra_vreg_spilledIn[vreg]=true;
								}

								ra_reg_table[expect].status=ALLOCATED;
								ra_reg_table[expect].mapped_to=vreg;
								ra_reg_table[expect].inst=vinst_seq;
								return expect;
}

// Force "vreg" NOT to be allocated to "except"
//
// Algorithm:
//	if vreg is already allocated and it's not "except"
//	    return immediately
//	else 
//	    "reserve" "except" register
//	    alloc = regAlloc(vreg, mode)
//	    "release" "except" register
//
XTRegNum SimpleRegAllocator::regAllocForceExcept(XTRegNum vreg, XTRegNum except, RegAccessMode mode)
{
								XTInt32 temp=ra_reg_table[except].status;
								XTRegNum alloc;
								if (temp== ALLOCATED && ra_reg_table[except].mapped_to == vreg)
								{				
																regSpillOut(except);
																temp=FREE;
								}
								ra_reg_table[except].status=RESERVED;
								alloc=regAlloc(vreg,mode);
								ra_reg_table[except].status=temp;

								return alloc;
}

void SimpleRegAllocator::regAllocReserve(XTRegNum treg)
{
								struct RegUsage &tru = ra_reg_table[treg];
								assert(tru.status != RegAllocator::RESERVED && "regAllocReserve"); 
								if(tru.inst==vinst_seq) printf("Can't reserve the reg here!!\n");
								if (tru.status == RegAllocator::ALLOCATED)
																regSpillOut(treg);
								tru.status = RegAllocator::RESERVED;
								tru.mapped_to = 0;
								return;
}


void SimpleRegAllocator::regAllocRelease(XTRegNum treg)// Release only affect the RESERVED Registers.
{
								struct RegUsage &tru = ra_reg_table[treg];
								assert(tru.status == RESERVED);
								tru.status = RegAllocator::FREE;
								return;
}

void SimpleRegAllocator::regSpillIn(XTRegNum vreg, XTRegNum treg)
{
								(*ra_cb_spill_in)((XTMemAddr)(ra_spill_pool+vreg), treg);
}


XTRegNum SimpleRegAllocator::regSpillOut(XTRegNum treg)
{
								assert(ra_reg_table[treg].status == RegAllocator::ALLOCATED);	

								XTRegNum vreg = ra_reg_table[treg].mapped_to;

								(*ra_cb_spill_out)(treg, (XTMemAddr)(ra_spill_pool+vreg));
								ra_vreg_spilledIn[vreg]=false;
								return treg;
}
